package com.ws.util;

import com.github.pagehelper.Dialect;
import com.github.pagehelper.dialect.AbstractDialect;
import com.github.pagehelper.util.MSUtils;
import com.github.pagehelper.util.SqlUtil;
import com.github.pagehelper.util.StringUtil;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author ：ws
 * @date ：Created in 2020/8/11 17:03
 * @description：TODO
 * @modified By：
 * @version: 1.0.0
 */
public class PaasSqlUtil extends SqlUtil {
    private Dialect dialect;
    private Field additionalParametersField;
    private Properties properties;
    private static final String SUFFIX = "_COUNT";
    private static final String WHERE11 = " WHERE 1=1";
    protected boolean autoDialect = true;
    protected boolean autoRuntimeDialect;
    protected boolean closeConn = true;
    private Map<String, Dialect> urlDialectMap = new ConcurrentHashMap();
    private ReentrantLock lock = new ReentrantLock();

    public Object intercept(Invocation invocation) throws Throwable {
        Object arg1;
        try {
            arg1 = this.doIntercept(invocation);
        } finally {
            clearLocalPage();
        }

        return arg1;
    }

    public Object doIntercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameterObject = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        if (this.autoDialect) {
            this.lock.lock();

            try {
                if (this.autoDialect) {
                    this.autoDialect = false;
                    this.dialect = this.getDialect(ms);
                }
            } finally {
                this.lock.unlock();
            }
        }

        Dialect runtimeDialect = this.dialect;
        if (this.autoRuntimeDialect) {
            runtimeDialect = this.getDialect(ms);
        }

        Object resultList;
        if (!runtimeDialect.skip(ms, parameterObject, rowBounds)) {
            ResultHandler resultHandler = (ResultHandler) args[3];
            Executor executor = (Executor) invocation.getTarget();
            BoundSql boundSql = ms.getBoundSql(parameterObject);
            Map additionalParameters = (Map) this.additionalParametersField.get(boundSql);
            CacheKey pageKey;
            if (runtimeDialect.beforeCount(ms, parameterObject, rowBounds)) {
                pageKey = executor.createCacheKey(ms, parameterObject, RowBounds.DEFAULT, boundSql);
                pageKey.update("_Count");
                MappedStatement pageSql = (MappedStatement) this.msCountMap.get(pageKey);
                if (pageSql == null) {
                    pageSql = MSUtils.newCountMappedStatement(ms);
                    this.msCountMap.put(pageKey, pageSql);
                }

                String pageBoundSql = runtimeDialect.getCountSql(ms, boundSql, parameterObject, rowBounds, pageKey);
                if (pageBoundSql.toUpperCase().endsWith("_COUNT")) {
                    pageBoundSql = pageBoundSql + " WHERE 1=1";
                }

                BoundSql countBoundSql = new BoundSql(ms.getConfiguration(), pageBoundSql,
                        boundSql.getParameterMappings(), parameterObject);
                Iterator key = additionalParameters.keySet().iterator();

                while (key.hasNext()) {
                    String count = (String) key.next();
                    countBoundSql.setAdditionalParameter(count, additionalParameters.get(count));
                }

                List key1 = executor.query(pageSql, parameterObject, RowBounds.DEFAULT, resultHandler, pageKey,
                        countBoundSql);
                Long count1 = (Long) ((List) key1).get(0);
                runtimeDialect.afterCount(count1.longValue(), parameterObject, rowBounds);
                if (count1.longValue() == 0L) {
                    return runtimeDialect.afterPage(new ArrayList(), parameterObject, rowBounds);
                }
            }

            if (runtimeDialect.beforePage(ms, parameterObject, rowBounds)) {
                pageKey = executor.createCacheKey(ms, parameterObject, rowBounds, boundSql);
                parameterObject = runtimeDialect.processParameterObject(ms, parameterObject, boundSql, pageKey);
                String pageSql1 = runtimeDialect.getPageSql(ms, boundSql, parameterObject, rowBounds, pageKey);
                BoundSql pageBoundSql1 = new BoundSql(ms.getConfiguration(), pageSql1, boundSql.getParameterMappings(),
                        parameterObject);
                Iterator countBoundSql1 = additionalParameters.keySet().iterator();

                while (countBoundSql1.hasNext()) {
                    String key2 = (String) countBoundSql1.next();
                    pageBoundSql1.setAdditionalParameter(key2, additionalParameters.get(key2));
                }

                resultList = executor.query(ms, parameterObject, RowBounds.DEFAULT, resultHandler, pageKey,
                        pageBoundSql1);
            } else {
                resultList = new ArrayList();
            }
        } else {
            args[2] = RowBounds.DEFAULT;
            resultList = (List) invocation.proceed();
        }

        return runtimeDialect.afterPage((List) resultList, parameterObject, rowBounds);
    }

    private Dialect initDialect(String dialectClass, Properties properties) {
        if (StringUtil.isEmpty(dialectClass)) {
            throw new RuntimeException("使用 PageHelper 分页插件时，必须设置 dialect 属性");
        } else {
            Dialect dialect;
            try {
                Class e = resloveDialectClass(dialectClass);
                if (AbstractDialect.class.isAssignableFrom(e)) {
                    dialect = (Dialect) e.getConstructor(new Class[]{SqlUtil.class}).newInstance(new Object[]{this});
                } else {
                    dialect = (Dialect) e.newInstance();
                }
            } catch (Exception arg4) {
                arg4.printStackTrace();
                throw new RuntimeException("初始化 dialect [" + dialectClass + "]时出错:" + arg4.getMessage());
            }

            dialect.setProperties(properties);
            return dialect;
        }
    }

    public String getUrl(DataSource dataSource) {
        Connection conn = null;

        String e;
        try {
            conn = dataSource.getConnection();
            e = conn.getMetaData().getURL();
        } catch (SQLException arg11) {
            throw new RuntimeException(arg11);
        } finally {
            if (conn != null) {
                try {
                    if (this.closeConn) {
                        conn.close();
                    }
                } catch (SQLException arg10) {
                    ;
                }
            }

        }

        return e;
    }

    public Dialect getDialect(MappedStatement ms) {
        DataSource dataSource = ms.getConfiguration().getEnvironment().getDataSource();
        String url = this.getUrl(dataSource);
        if (this.urlDialectMap.containsKey(url)) {
            return (Dialect) this.urlDialectMap.get(url);
        } else {
            Dialect dialectStr;
            try {
                this.lock.lock();
                if (!this.urlDialectMap.containsKey(url)) {
                    if (StringUtil.isEmpty(url)) {
                        throw new RuntimeException("无法自动获取jdbcUrl，请在分页插件中配置dialect参数!");
                    }

                    String dialectStr1 = fromJdbcUrl(url);
                    if (dialectStr1 == null) {
                        throw new RuntimeException("无法自动获取数据库类型，请通过 dialect 参数指定!");
                    }

                    Dialect dialect = this.initDialect(dialectStr1, this.properties);
                    this.urlDialectMap.put(url, dialect);
                    Dialect arg5 = dialect;
                    return arg5;
                }

                dialectStr = (Dialect) this.urlDialectMap.get(url);
            } finally {
                this.lock.unlock();
            }

            return dialectStr;
        }
    }

    public void setProperties(Properties properties) {
        super.setProperties(properties);
        String closeConn = properties.getProperty("closeConn");
        if (StringUtil.isNotEmpty(closeConn)) {
            this.closeConn = Boolean.parseBoolean(closeConn);
        }

        String dialect = properties.getProperty("dialect");
        String runtimeDialect = properties.getProperty("autoRuntimeDialect");
        if (StringUtil.isNotEmpty(runtimeDialect) && runtimeDialect.equalsIgnoreCase("TRUE")) {
            this.autoRuntimeDialect = true;
            this.autoDialect = false;
            this.properties = properties;
        } else if (StringUtil.isEmpty(dialect)) {
            this.autoDialect = true;
            this.properties = properties;
        } else {
            this.autoDialect = false;
            this.dialect = this.initDialect(dialect, properties);
        }

        try {
            this.additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
            this.additionalParametersField.setAccessible(true);
        } catch (NoSuchFieldException arg5) {
            throw new RuntimeException(arg5);
        }
    }
}